﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ProcessTradingHistory
{

    public enum TradeAction
    {
        BUY = 0,
        SELL,
        SELLSHORT,
        BUYCOVER
    }

    public enum LongOrShort
    {
        LONG = 0,
        SHORT = 1
    }

    public class Position
    {
        public LongOrShort longshort;
        public DateTime tradeDate;
        public Double quantity;
        public String security;
        public Double costBasis;

        public Position(LongOrShort longshort, String security, Double cost, DateTime date, Double quantity)
        {
            this.longshort = longshort;
            this.tradeDate = date;
            this.quantity = quantity;
            this.security = security;
            this.costBasis = cost;
        }
    }

    public class Transaction : IComparable<Transaction>
    {
        public TradeAction action;
        public DateTime tradeDate;
        public Double amount;
        public Double tradePrice;
        public String security;
        public Double quantity;

        public Transaction(string line)
        {
            string[] tokens = line.Split(',');
            if (tokens[0].IndexOf("Pending") >= 0)
            {
                tokens[0] = tokens[0].Substring(0, tokens[0].IndexOf("Pending"));
            }
            string[] date = tokens[0].Split('/');
            tradeDate = new DateTime(Convert.ToInt32(date[2]), Convert.ToInt32(date[0]), Convert.ToInt32(date[1]));

            if (tokens[3].Contains("Buy ") || tokens[3].Contains("Purchase "))
            {
                action = TradeAction.BUY;
            }
            else if (tokens[3].Contains("Sale ") || tokens[3].Contains("Sell "))
            {
                action = TradeAction.SELL;
            }

            security = tokens[4];
            quantity = Convert.ToDouble(tokens[5]);
            tradePrice = Convert.ToDouble(tokens[6]);

            string []amountSplit = line.Split('"');
            if (amountSplit.Length >= 3)
            {
                amount = Convert.ToDouble(amountSplit[1]);
            }
            else
            {
                amount = Convert.ToDouble(tokens[7]);
            }
        }

        public int CompareTo(Transaction y)
        {
            return this.tradeDate < y.tradeDate ? -1 : 1;
        }

        public Boolean IsOpenPosition()
        {
            return (TradeAction.BUY == action || TradeAction.SELLSHORT == action);
        }

        public LongOrShort getLongOrShort()
        {
            if (action == TradeAction.BUY || action == TradeAction.SELL)
                return LongOrShort.LONG;
            else return LongOrShort.SHORT;
        }
    }


    public class Book
    {

        public Dictionary<string, List<Position>> positions = new Dictionary<string, List<Position>>();

        public void addTrade(Transaction newTransaction)
        {
            if (newTransaction.action == TradeAction.BUY || newTransaction.action == TradeAction.SELLSHORT)
                addPosition(newTransaction);
            else closePosition(newTransaction);
        }

        public void addPosition(Transaction newTransaction)
        {
            if (newTransaction.action == TradeAction.BUY || newTransaction.action == TradeAction.SELLSHORT)
            {
                Position p = new Position(newTransaction.getLongOrShort(), newTransaction.security, newTransaction.tradePrice, newTransaction.tradeDate, newTransaction.quantity);
                if (positions.ContainsKey(p.security))
                {
                    List<Position> alreadyExistPosList = positions[p.security];
                    bool samePosition = false;
                    foreach (Position existPos in alreadyExistPosList)
                    {
                        if (existPos.longshort == p.longshort || newTransaction.tradePrice == existPos.costBasis)
                        {
                            existPos.quantity += p.quantity;
                            samePosition = true;
                        }
                    }
                    if (samePosition == false)
                    {
                        positions[p.security].Add(p);
                    }
                }
                else
                {
                    List<Position> newPos = new List<Position>();
                    newPos.Add(p);
                    positions.Add(p.security, newPos);
                }
            }
        }

        public void closePosition(Transaction record)
        {
            if (record.action == TradeAction.SELL || record.action == TradeAction.BUYCOVER)
            {
                if (positions.ContainsKey(record.security))
                {
                    List<Position> posList = positions[record.security];
                    bool foundOpenPosition = false;
                    List<int> matchPosIndex = new List<int> ();
                    for (int i = 0; i < posList.Count; i++)
                    {
                        if (posList[i].longshort == record.getLongOrShort())
                        {
                            //foundOpenPosition = true;
                            //posList[i].quantity -= record.quantity;
                            //if (posList[i].quantity < 0)
                            //{
                            //    Console.WriteLine("Position " + record.security + " is greater than existing position");
                            //}
                            //else if (posList[i].quantity == 0)
                            //{
                            //    matchPosIndex = i;
                            //}
                            //break;
                        }
                    }

                    //if (matchPosIndex >= 0)
                    //{
                    //    posList.RemoveAt(matchPosIndex);
                    //}
                    //if (posList.Count == 0)
                    //{
                    //    positions.Remove(record.security);
                    //}

                    if (foundOpenPosition == false)
                    {
                        Console.WriteLine("Position " + record.security + " cannot be closed");
                    }
                }
                else
                {
                    Console.WriteLine(record.security + " doesn't have open position");
                }
            }
        }
    }


    class Program
    {


        static void Main(string[] args)
        {
            //string aggFile = args[0];
            //string inputFile = args[1];
            //IEnumerable<string> aggInputs = File.ReadLines(aggFile);
            //IEnumerable<string> newInputs = File.ReadLines(inputFile);

            List<Transaction> records = new List<Transaction>();
            Book book = new Book();

            StreamReader readFile = new StreamReader(@"C:\Users\bahamut\Documents\Stock\History\test.csv");
            while(readFile.Peek() >= 0)
            {
                string line = readFile.ReadLine();
                if (line.Contains("Buy ") || line.Contains("Sell ") || line.Contains("Purchase ") || line.Contains("Sale "))
                {
                    Transaction record = new Transaction(line);
                    records.Add(record);
                }
            }

            records.Sort(delegate(Transaction x, Transaction y)
            {
                return x.CompareTo(y);
            });


            List<Transaction> currentDayOpenPositions = new List<Transaction>();
            List<Transaction> currentDayClosePositions = new List<Transaction>();
            DateTime currentDate = records[0].tradeDate;

            foreach (Transaction record in records)
            {
                if (record.tradeDate == currentDate)
                {
                    if(record.IsOpenPosition())
                    {
                        currentDayOpenPositions.Add(record);
                    }
                    else
                    {
                        currentDayClosePositions.Add(record);
                    }
                }
                else
                {
                    currentDate = record.tradeDate;
                    foreach (Transaction r in currentDayOpenPositions)
                    {
                        book.addTrade(r);
                    }
                    foreach (Transaction r in currentDayClosePositions)
                    {
                        book.addTrade(r);
                    }

                    if (record.IsOpenPosition())
                    {
                        currentDayOpenPositions.Add(record);
                    }
                    else
                    {
                        currentDayClosePositions.Add(record);
                    }
                }
            }
        }
    }
}
